/*
 * H264PropParser.cpp
 *
 *  Created on: 2012-3-16
 *      Author: terry
 */

#include "H264PropParser.h"
#include "Base64.h"
#include <ctype.h>

namespace av
{


NaluPacket::NaluPacket():
        data(),
        length(),
        type(),
        prefix()
{
}

NaluPacket::~NaluPacket()
{
}








const uint8_t H264PropParser::s_startCode[START_CODE_LENGTH] = { 0, 0, 0, 1};


H264PropParser::H264PropParser()
{
}

H264PropParser::~H264PropParser()
{
}

const uint8_t* H264PropParser::getStartCode()
{
    return s_startCode;

}


bool H264PropParser::startWithCode(const uint8_t* data, size_t length)
{
	if (length < 4)
	{
		return false;
	}

	if (data[0] == 0 && data[1] == 0)
	{
		if (data[2] == 1)
		{
			return true;
		}
		else if ((data[2] == 0) && (data[3] == 1))
		{
			return true;
		}
	}

	return false;
}

#define RB16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])

bool H264PropParser::splitPropSet(const uint8_t* p, size_t len,
            std::string& sps, std::string& pps)
{
    if (len == 0)
    {
        return false;
    }

    if (startWithCode(p, len))
    {
        size_t start = 4;
        size_t nextPos = findStartCode(p, len, start);
        if (nextPos == size_t(-1))
        {
            return false;
        }
        sps = std::string((const char*)(p+start), nextPos - start);

        start = nextPos + 4;
        nextPos = findStartCode(p, len, start);
        if (nextPos == size_t(-1))
        {
            nextPos = len;
        }
        pps = std::string((const char*)(p+start), nextPos - start);
        return true;
    }

    int i, cnt, nalsize;
    const uint8_t *q = p;
    cnt = *(p+5) & 0x1f; // Number of sps
    p += 6;

    for (i = 0; i < cnt; i++) {
        if (p > q + len)
            return false;
        nalsize = RB16(p); //buf_size
        p += 2;
		sps.assign((char*)p, nalsize);
        p += nalsize;
    }
    // Decode pps from avcC
    cnt = *(p++); // Number of pps

    for (i = 0; i < cnt; i++) {

        if (p > q + len)
            return false;
        nalsize = RB16(p);
        p += 2;
        pps.assign((char*)p, nalsize);
        p += nalsize;
    }

    return true;
}

bool H264PropParser::splitPropSet(const std::string& sprop, std::string& sps, std::string& pps)
{
    return splitPropSet((const uint8_t*)sprop.c_str(), sprop.length(), sps, pps);
}

bool H264PropParser::getSprop(const uint8_t* p, size_t len, std::string& sprop)
{
    if (startWithCode(p, len))
    {
        uint8_t buffer[6] = {1, 0, 0x00, 0, 0xFF, 0xE1};
        sprop.assign((char*)buffer, 6);

        size_t start = 4;
        size_t nextPos = findStartCode(p, len, start);
        if (nextPos == size_t(-1))
        {
            return false;
        }
        size_t length = nextPos - start;
        sprop += (char)(length >> 8);
        sprop += (char)length;

        sprop.append((const char*)(p+start), length);

        sprop[1] = (char)p[start+1]; // profile
        sprop[3] = (char)p[start+3];    // level

        start = nextPos + 4;
        nextPos = findStartCode(p, len, start);
        if (nextPos == size_t(-1))
        {
            nextPos = len;
        }
        length = nextPos - start;
        sprop += (char)1;
        sprop += (char)(length >> 8);
        sprop += (char)length;

        sprop.append((const char*)(p+start), length);
    }
    else
    {
        sprop.assign((const char*)p, len);
    }
    return true;
}

int H264PropParser::removeEscape(const uint8_t* data, size_t length, std::string& nalu)
{
    int count = 0;
    for (size_t i = 0; i < length; i ++)
    {
        if (data[i] == 0x03)
        {
            if (i >= 2)
            {
                if (data[i-1] == 0 && data[i-2] == 0)
                {
                    count ++;
                    continue;
                }
            }
        }

        nalu += (char)data[i];
    }
    return count;
}

int H264PropParser::removeEscape(std::string& nalu)
{
    int count = 0;
    size_t pos = nalu.find(0x03, 0);
    while (pos != std::string::npos)
    {
        if (pos >= 2 && nalu[pos - 1] == 0 && nalu[pos - 2] == 0)
        {
            nalu.erase(pos, 1);
        }

        pos = nalu.find(0x03, pos);
    }
    return count;
}

void H264PropParser::combineToSprop(const std::string& sps, const std::string& pps,
            int profile, int level, std::string& sprop)
{
    uint8_t buffer[6] = {1, (uint8_t)profile, 0x00, (uint8_t)level, 0xFF, 0xE1};
    sprop.assign((char*)buffer, 6);

    size_t length = sps.size();
    sprop += (char)(length >> 8);
    sprop += (char)length;

    sprop += sps;

    length = pps.size();
    sprop += (char)1;
    sprop += (char)(length >> 8);
    sprop += (char)length;

    sprop += pps;

}

void H264PropParser::combineToSprop(const std::string& sps, const std::string& pps, std::string& sprop)
{
    int profile = sps[1];
    int level = sps[3];
    combineToSprop(sps, pps, profile, level, sprop);
}

void H264PropParser::combineToSprop(const uint8_t* sps, size_t spsLength,
            const uint8_t* pps, size_t ppsLength, std::string& sprop)
{
    int profile = sps[1];
    int level = sps[3];

    uint8_t buffer[6] = {1, (uint8_t)profile, 0x00, (uint8_t)level, 0xFF, 0xE1};
    sprop.assign((char*)buffer, 6);

    size_t length = spsLength;
    sprop += (char)(length >> 8);
    sprop += (char)length;

    sprop.append((const char*)sps, spsLength);

    length = ppsLength;
    sprop += (char)1;
    sprop += (char)(length >> 8);
    sprop += (char)length;

    sprop.append((const char*)pps, ppsLength);
}


size_t H264PropParser::findStartCode(const uint8_t* data, size_t length, size_t start)
{
    for (size_t i = start; i < length - 4; ++ i)
    {
        if (data[i] == 0)
        {
            if (startWithCode(&data[i], length - i))
            {
                return i;
            }
        }
    }
    return -1;
}

void H264PropParser::insertStartCode(std::string& data)
{
    data.insert(0, (const char*)s_startCode, START_CODE_LENGTH);
}

bool H264PropParser::parseH264ProfileLevel(const std::string& hexStr, int& profile, int& level)
{
    bool done = false;
    if (hexStr.size() == 6)
    {
        std::string data;
        hexToData(hexStr, data);

        if (data.size() == 3)
        {
            profile = data[0];
            level = data[2];
        }

        done = true;
    }
    return done;
}




size_t H264PropParser::hexToData(const std::string& hexStr, std::string& data)
{
    size_t length = 0;
    int c = 0;
    int value = 1;
    for (size_t i = 0; i < hexStr.size(); i ++)
    {
        c = toupper((unsigned char)hexStr[i]);
        if (c >= '0' && c <= '9')
            c = c - '0';
        else if (c >= 'A' && c <= 'F')
            c = c - 'A' + 10;
        else
            break;
        value = (value << 4) | c;
        if (value & 0x100)
        {
            data.append(1, value);

            length ++;
            value = 1;
        }
    }
    return length;
}


bool H264PropParser::extractFromSdp(const char* sprop, size_t length, std::string& sps, std::string& pps)
{
    if ((sprop == NULL) || (length == 0))
    {
        return false;
    }

    const char * p = strchr(sprop, ',');
    if (p == NULL)
    {
        return false;
    }

    //*(const_cast< char* >(p)) = '\0';

    uint8_t buffer[1024] = {0};
    int size = comn::Base64::decode(buffer, 1024, sprop, (p - sprop));
    sps.assign((char*)buffer, size);

    size = comn::Base64::decode(buffer, 1024, p + 1, (sprop + length - p - 1));
    pps.assign((char*)buffer, size);

    //*(const_cast< char* >(p)) = ',';

    return true;
}

bool H264PropParser::isKeyFrame(uint8_t nalu)
{
    return ((nalu & 0x1F) == 5);
}

bool H264PropParser::parseNalu(const uint8_t* data, size_t length, NaluPacket& pkt)
{
    if ((data[0] == 0) && (data[1] == 0))
    {
        if (data[2] == 1)
        {
            pkt.prefix = 3;
        }
        else if ((data[2] == 0) && (data[3] == 1))
        {
            pkt.prefix = 4;
        }
        else
        {
            return false;
        }
    }

    pkt.data = data + pkt.prefix;
    pkt.length = length - pkt.prefix;
    pkt.type = (pkt.data[0] & 0x1F);

    return true;
}

int H264PropParser::parseNaluType(uint8_t data)
{
    return (data & 0x1F);
}





}
